/**
 * Simple prolog mashine
 * Author: Bobrikovich, Kamenkov
 *
 * About Boost Spirit
 * 
 * carefully using a combination of using namespace & namespace statements
 * if you get with
 *
 * test data: P(X,Y)<-N(Y,X);M(X,X);L(Y,Y);K(X,Y).
 *
 */
#include "interpreter.hpp"
#include "logger.hpp"
#include "utils.hpp"

namespace mashine { namespace input {

using namespace boost::spirit;
using namespace boost::phoenix;

/**
 * Grammar constructor
 */
Interpreter::Grammar::Grammar()
		: qi::grammar<std::string::const_iterator, unsigned(), 
				ascii::space_type>::base_type(allRules, "input") {
	
	// placeholders
	using qi::_1;
	using qi::_2;
	using qi::_3;
	using qi::_4;
	using qi::_val;

	// character parsers
	using qi::char_;
	using qi::alpha;
	using ascii::lower;
	using ascii::upper;

	// for error handling
	using qi::on_error;
	using qi::fail;

	// semantic actions

	//
	// Fact parser
	//
	lowerWord 	= 	(+lower) [action(_val, _1)]
				;
	upperWord 	= 	(+upper) [action(_val, _1)]
				;
	fact 		= 	(upperWord >> '(' >> lowerWord % ',') 
					[action(_val, _1, _2)] 
					
				>> 	')' >> '.'
				;


	//
	// Analogy parser
	//
	upperPair 	= 	(upperWord >> ',' >> upperWord)
					[action(_val, _1, _2)]
				;
	lowerPair 	= 	(lowerWord >> ',' >> lowerWord)
					[action(_val, _1, _2)]
				;
	analogy 	%= 	(lowerWord >> '(' >> (upperPair | lowerPair))
					[action(_val, _1, _2)]
					
				>> 	')' >> '.'
				;


	//
	// Rule parser
	//
	Literal 	%= 	(upperWord >> '(' >>
				(
					upperWord | lowerWord
				)
				% ',') [action(_val, _1, _2)] >> ')'
				;
	canon 		= 	(Literal >> lit("<-") >> Literal % ';') 
					[action(_val, _1, _2)] 
					
				>> 	'.'
				;



	//
	// Goal parser
	//
	goal		%=	(Literal % ',') [action(_val, _1)] >> '.'
				;
			

	command		%=	(lit("dump")) [action("$dump")]
				;
			
	//
	// Start rule
	//
	allRules 	%= 	(fact 	 	[action(_1)]) [_val = 1]
				| 	(analogy 	[action(_1)]) [_val = 2]
				| 	(canon 		[action(_1)]) [_val = 3]
				|	(goal		[action(_1)]) [_val = 4]
				|	("$" >> command			) [_val = 5]
				;
	
	// set names to rules
	allRules.name("input");
	Literal.name("Literal");
	analogy.name("analogy");
	canon.name("canon");
	fact.name("fact");

	// error handling		
	on_error<fail> (fact, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);

	on_error<fail> (analogy, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);
			
	on_error<fail> (canon, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);
			
	on_error<fail> (goal, std::cout
			<< val("Error! Expecting ")
			<< qi::_4 									// what failed?
			<< val(" here: \"")
			<< construct<std::string>(qi::_3, qi::_2) 	// iterators to error-pos, end
			<< val("\"")
			<< std::endl);


	APP_LOGGER(info) << "Grammar instantiated";
}

/**
 * Grammar destructor
 */
Interpreter::Grammar::~Grammar() {
	APP_LOGGER(info) << "Grammar destroyed";
}

/**
 * Analogy build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Analogy & analogy, std::string name, std::vector<std::string> & params) const
{
	APP_LOGGER(info) << "Analogy action. init address " << params[0] << " " << params[1];

	analogy = mashine::kb::Analogy(name, params[0], params[1]);

	APP_LOGGER(info) << "Analogy build : " << analogy.toString();
}

/**
 * Pair (lower or upper) build
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<std::string> & pair, std::string name1, std::string name2) const 
{	
	// APP_LOGGER(info) << "Pair action. init address " << pair;

	pair = std::vector<std::string>();
	
	pair.push_back(name1);
	pair.push_back(name2);
	
	APP_LOGGER(info) << "Pair build : " << pair[0] << " " << pair[1];
}

/**
 * Word (lower or upper) build
 */
void Interpreter::Grammar::Semantic::operator()(std::string & word, std::vector<char> & chars) const
{
	// APP_LOGGER(info) << "Word action. init address " << word;
	
	word = std::string(chars.begin(), chars.end());

	// APP_LOGGER(info) << "Word build : " << *word;
}

/**
 * Rule build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Rule & rule, mashine::kb::Literal notDenied, std::vector<mashine::kb::Literal> & denied) const
{
	// APP_LOGGER(info) << "Rule action. init address " << rule << " name=" << notDenied->toString();
	
	BOOST_FOREACH (mashine::kb::Literal & l, denied)
	{
		l.setDenied(true);
	}
	
	// need to create new vector because
	std::vector<mashine::kb::Literal> allLiterals(denied);	// need copy of vector
	allLiterals.push_back(notDenied);
	
	rule.setParams(allLiterals);
	APP_LOGGER(info) << "Rule build : " << rule.toString();
}

/**
 * Fact of Literal build
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Literal & literal, std::string name, std::vector<std::string> & params) const
{
	// APP_LOGGER(debug) << "Analogy action. init address " << literal;

	literal = mashine::kb::Literal(name, params, false);

	// APP_LOGGER(debug) << "Literal build : " << literal->toString();
}

/**
 * Goal build
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<mashine::kb::Literal> & goal, std::vector<mashine::kb::Literal> & goal_) const
{
	goal = std::vector<mashine::kb::Literal>(goal_);
}

/**
 *  Inference goal 
 */
void Interpreter::Grammar::Semantic::operator()(std::vector<mashine::kb::Literal> const & goal) const
{
	APP_LOGGER(debug) << "Goal to inference " << utils::vector_to_string<mashine::kb::Literal>(goal);
	
	std::vector<mashine::kb::Literal> result = Interpreter::instance()->getBase()->inference(goal);
	
	// APP_LOGGER(debug) << &result ;
	// APP_LOGGER(debug) << utils::vector_to_string<mashine::kb::Literal>(result);
	
	std::string msg = utils::vector_to_string<mashine::kb::Literal>(goal);
	msg += " result ";
	APP_LOGGER(info) << "<<< !!! >>>";
	msg += utils::vector_to_string<mashine::kb::Literal>(result);
	
	APP_LOGGER(info) << "<<< !!! >>>";
	
	Interpreter::instance()->setLastMessage(msg);
	
}

/**
 * Add entity 
 */
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Literal & entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity.toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
		
	APP_LOGGER(info) << "Literal added : " <<  entity.toString();
}
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Analogy & entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity.toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
	
	APP_LOGGER(info) << "Analogy added : " <<  entity.toString();
}
void Interpreter::Grammar::Semantic::operator()(mashine::kb::Rule & entity) const
{
	Interpreter::instance()->getBase()->add(entity);
	
	std::string msg = entity.toString();
	msg += " added.";
	Interpreter::instance()->setLastMessage(msg);
	
	APP_LOGGER(info) << "Rule added : " << entity.toString();
}

void Interpreter::Grammar::Semantic::operator()(std::string command) const
{
	APP_LOGGER(info) << "Dump of KB : ";
	
	std::string msg = "facts : " +  utils::vector_to_string<mashine::kb::Literal>(Interpreter::instance()->getBase()->getFacts()) + "\n";
	msg += "analogies : " + utils::vector_to_string<mashine::kb::Analogy>(Interpreter::instance()->getBase()->getAnalogies()) + "\n";
	msg += "rules : " + utils::vector_to_string<mashine::kb::Rule>(Interpreter::instance()->getBase()->getRules()) + "\n";
	
	Interpreter::instance()->setLastMessage(msg);
}

Interpreter* Interpreter::inst = NULL; // pay attention to the position of stars

Interpreter::Interpreter() {
	APP_LOGGER(info) << "Interpreter instantiated";
}

Interpreter* Interpreter::instance() {
	if (inst == NULL) {
		inst = new Interpreter();
	}
	return inst;
}

std::string & Interpreter::interprete(std::string const & input) {
	message = "";

	std::string::const_iterator first = input.begin();
	std::string::const_iterator last = input.end();

	APP_LOGGER(info) << "Begin interpreting " << input;

	unsigned entityType;
	
	bool result = qi::phrase_parse(first, last, gramm, ascii::space, entityType);
	
	APP_LOGGER(info) << "End interpreting " << input;
	
	if (result) {
		
		return this->message;
	
	} else {
		message = "parsing failed. stopped at: \": ";
		message += std::string(first, last);
		message += "\"\n";
		
		return message;
	}
}

void Interpreter::setBase(kb::KnowledgeBase * kBase) 
{
	this->kBase = kBase;
}

kb::KnowledgeBase* Interpreter::getBase() 
{
	return this->kBase;
}

std::string Interpreter::getLastMessage() 
{
	return (this->message);
}

void Interpreter::setLastMessage(std::string & msg)
{
	this->message = msg;
}

Interpreter::~Interpreter() {
	APP_LOGGER(info) << "Interpreter ";
}

}}